package cz.uhk.max.matrix;

import java.util.ArrayList;
import java.util.List;

import cz.uhk.max.matrix.impl.MatrixDao;

//A*x = B*x , find x
public class Exercise1 {
private Matrix A;
private Matrix B;
private MatrixDao dao;
private Long infinite;

public Exercise1 (Matrix A, Matrix B, Long infinite){
	//matice A a B mus� m�t stejn� po�et ��dk� a sloupc�
	this.A = new Matrix(A);
	this.B = new Matrix(B);
	dao = new MatrixDao();
	this.setInfinite(infinite);
}


public Matrix returnMaxX() {
	List<Matrix> candidatesForX = new ArrayList<Matrix>();
	for (int i = 0; i < A.getRowsCount(); i++) {
		//postupn� se zjist� matice x pro ka�d� ��dek,
		//kter� lze pou��t pro nalezen� schodn�ch v�sledk� n�soben�
		//pokud se n�sob� t�mto ��slem matice A a matice B
		candidatesForX.add(findXcandidates(i));
	}
	//z nalezen�ch �e�en� je nutn� pro ka�d� j-t� index v matici x
	//vybrat to men�� ��slo
	return findMinValuesInXCandidates(candidatesForX);
}

public Matrix findXcandidates(Integer rowIndex){
	//pro ka�d� ��dek (RowIndex) se porovn� matice A a matice B
	//nejprve se zjist�, kter� matice obsahuje v ��dku vy��� ��slo
	//matice x bude m�t v�dy tolik ��dk�, kolik maj� p�vodn� matice sloupc�
	Col maxA = new Col();
	Col maxB = new Col();
	maxA = dao.maximum(A.getRows().get(rowIndex).getCols());
	maxB = dao.maximum(B.getRows().get(rowIndex).getCols());

	if (maxA.getValue() > maxB.getValue()){
		//System.out.println("max A:" + maxA.getValue() + "max B: " + maxB.getValue());
		//v�t�� matice se mus� o��znout, tedy:
		//na maxim�ln� hodnotu z B se mus� o��znout ka�d� A,
		//kter� je v�t��, ne� tato maxim�ln� hodnota z B
		//zjistime maticici xCandidate tak, �e zjist�me, kter� pozice z 
		//z matice A jsou v�t�� ne� max z matice B,
		//a na t�chto pozic�ch bude m�t x maxB
		return xCandidate(A,B,rowIndex, maxB.getValue());
	} else
	{
		return xCandidate(B,A,rowIndex, maxA.getValue());
	}
	}

private Matrix xCandidate(Matrix vetsiMatice, Matrix mensiMatice, Integer rowIndex, Long maxMensiMatice) {
	Matrix xCandidate = new Matrix (vetsiMatice.getColsCount(),1);
	//na za��tku se hodnoty matice xCandidate nastaven� na nekone�no
	for (int i = 0; i < xCandidate.getRowsCount(); i++) {
		xCandidate.setCol(i, 0, this.infinite);
	}
	for (int i = 0; i < vetsiMatice.getColsCount(); i++) {
		if (vetsiMatice.getCol(rowIndex, i).getValue() > maxMensiMatice) {
			xCandidate.setCol(i, 0, maxMensiMatice);
		}
	}
	return xCandidate;
}

public Matrix returnX(){
	List<Matrix> candidatesForX = new ArrayList<Matrix>();
	for (int i = 0; i < A.getRowsCount(); i++) {
		//postupn� se zjist� matice x pro ka�d� ��dek,
		//kter� lze pou��t pro nalezen� schodn�ch v�sledk� n�soben�
		//pokud se n�sob� t�mto ��slem matice A a matice B
		candidatesForX.add(findXcandidateForRow(i));
	}
	//z nalezen�ch �e�en� je nutn� pro ka�d� j-t� index v matici x
	//vybrat to men�� ��slo
	return findMinValuesInXCandidates(candidatesForX);
}

public Matrix findXcandidateForRow (Integer RowIndex) {
	//pro ka�d� j-t� ��dek dvou matic a vezme se men�� ��slo 
	//v�sledkem je matice X, kterou lze vyn�sobit tento ��dek
	//jak v matici A tak v matici B se a v�sledek je schodn�
	//Ai*x = Bi*x
	Matrix x = new Matrix(A.getColsCount(),1);
	//x m� tolik ��dk� kolik m� A sloupc�
	for (int j = 0; j < A.getColsCount(); j++) {
		if (A.getCol(RowIndex, j).getValue() <= B.getCol(RowIndex, j).getValue()) {
			//pokud je hodnota v matici A men�� ne� hodnota v matici B, vezmi hodnotu z A	
			x.setCol(j, 0, A.getCol(RowIndex, j).getValue());
		} else {
			//vlo� hodnotu z matice B, pokud je B men�� ne� A
			x.setCol(j, 0, B.getCol(RowIndex, j).getValue());
		}
	}
	return x;
}

public Matrix findMinValuesInXCandidates(List<Matrix> xCandidates) {
	List<Matrix> candidates = new ArrayList<Matrix>(xCandidates);
	Matrix x = new Matrix(A.getColsCount(),1);
	
	List<Long> listOfXi = new ArrayList<Long>();
	
		for (int j = 0; j < A.getColsCount(); j++) {
			//proch�z� se v�echny ��dky, tedy xi
			//x m� tolik ��dk�, kolik je sloupc� v matici A
			//v tomto cyklu se pro ka�d� xi vytvo�� seznam
			//z tohoto seznamu se vybere nejmen�� x
			for (int i = 0; i < candidates.size(); i++) {
				//vyb�r� se hodnota z kandid�ta j, a z jeho ��dku
				listOfXi.add(candidates.get(i).getCol(j, 0).getValue());
			}
			//	System.out.println("minimum pro x" + j + " " + minimum(listOfXi));
			x.setCol(j, 0, minimum(listOfXi));
			listOfXi.clear();
		}
		//kdy� je vybr�n seznam kandid�lu, je z n�ho vybr�no nejv�t�� ��slo
		//do v�sledn�ho x je na dan� index i ulo�ena nejmen�� nalezen� hodnota
		
	return x;
}

private Integer maximum (List<Integer> mins) {
	//pro nalezen� maxima ze seznamu minim
	//metoda vrac� sloupec Col s maxim�ln� hodnotou
	List<Integer> minsList = new ArrayList<Integer>(mins);
	Integer pom;
	  for (int i = 0; i < (minsList.size()-1); i++) {
	      if (minsList.get(i+1) < minsList.get(i)) {
	        pom = minsList.get(i);
	        minsList.set(i, minsList.get(i+1));
	        minsList.set(i+1,pom);
	      }
	    }
	return minsList.get(minsList.size()-1);
}

private Long minimum (List<Long> mins) {
	//pro nalezen� maxima ze seznamu minim
	//metoda vrac� sloupec Col s maxim�ln� hodnotou
	List<Long> minsList = new ArrayList<Long>(mins);
	Long pom;
	  for (int i = 0; i < (minsList.size()-1); i++) {
	      if (minsList.get(i+1) > minsList.get(i)) {
	        pom = minsList.get(i);
	        minsList.set(i, minsList.get(i+1));
	        minsList.set(i+1,pom);
	      }
	    }
	return minsList.get(minsList.size()-1);
}

private Integer minimum (List<Integer> mins) {
	//pro nalezen� maxima ze seznamu minim
	//metoda vrac� sloupec Col s maxim�ln� hodnotou
	List<Integer> minsList = new ArrayList<Integer>(mins);
	Integer pom;
	  for (int i = 0; i < (minsList.size()-1); i++) {
	      if (minsList.get(i+1) > minsList.get(i)) {
	        pom = minsList.get(i);
	        minsList.set(i, minsList.get(i+1));
	        minsList.set(i+1,pom);
	      }
	    }
	return minsList.get(minsList.size()-1);
}

public Matrix getA() {
	return A;
}

public void setA(Matrix a) {
	A = a;
}

public Matrix getB() {
	return B;
}

public void setB(Matrix b) {
	B = b;
}

public void setInfinite(Long infinite) {
	this.infinite = infinite;
}

public Long getInfinite() {
	return infinite;
}
}
